import pandas as pd
import info
import numpy as np
import datetime
from GetExternalDataSourceModule_CLIENT import GetExternalDataSource_CLIENT
class TradeRecord:
    def __init__(self,
                 trade_unit,
                 stop_win,
                 stop_loss,
                 raw_data_ZL,
                 raw_data_ZL_last,
                 x_min_price,
                 rotate_signal,
                 clearing_time,
                 rotate_trade_name,
                 max_trade_times,
                 long_freeze_period,
                 short_freeze_period,
                 ):
        self.connector = GetExternalDataSource_CLIENT()
        self.position = {}
        self.count = 1
        self.trade_unit = trade_unit
        self.stop_win = stop_win
        self.stop_loss = stop_loss
        self.raw_data = raw_data_ZL
        self.raw_data_ZL_last = raw_data_ZL_last
        self.x_min_price = x_min_price
        self.rotate_signal = rotate_signal
        self.clearing_time = clearing_time
        self.rotate_trade_name = rotate_trade_name
        self.contract_info = self.connector.get_file('ContractInfo',
                                                     info.DATAPATH.server_root_path + '/' + 'future_basic_data' + '/' + 'ContractInfo.pkl')

        self.current_date = 0
        self.max_trade_times = max_trade_times
        self.long_freeze_period = long_freeze_period
        self.short_freeze_period = short_freeze_period
        self.next_long_datetime = datetime.datetime.strptime('2000-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
        self.next_short_datetime = datetime.datetime.strptime('2000-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
        self.clearing_daily=True

        
    def set_current_bar_trade_info(self,
                                   contract_name,
                                   close_position_price,
                                   open_position_price,
                                   last_bar_price,
                                   current_bar_datetime,
                                   daily_clearing_position_price=None
                                   ):
        self._contract_name = contract_name
        self._close_position_price = close_position_price
        self._open_position_price = open_position_price
        self._last_bar_price = last_bar_price
        self._current_bar_datetime = current_bar_datetime
        self._daily_clearing_position_price = daily_clearing_position_price
        current_date_tmp = str(current_bar_datetime)[:10]
        if self.current_date != current_date_tmp:
            self.new_day = True
            self.current_date = current_date_tmp
            self.trade_count_daily = 0
        else:
            self.new_day = False

        self.rotate_flag = self.rotate_signal[current_date_tmp]
        if len(self.clearing_time)>0:
            self._clearing_time = [(datetime.datetime.strptime(self.current_date + ' ' + x.split('_')[0], '%Y-%m-%d %H%M%S'),
                                    datetime.datetime.strptime(self.current_date + ' ' + x.split('_')[1], '%Y-%m-%d %H%M%S')
                                    ) for x in self.clearing_time]
        else:
            self._clearing_time = []


    def update_current_position(self,):
        tradeBook = self.get_position()
        if not tradeBook.empty:
            active_trades = tradeBook.loc[tradeBook.status == 'On']
            for index, row in active_trades.iterrows():
                trade_direction = active_trades.loc[index, 'position']
                trade_init_price = active_trades.loc[index, 'init_price']
                if trade_direction=='long':
                    tmp_ret = self._last_bar_price / trade_init_price - 1
                elif trade_direction == 'short':
                    tmp_ret = 1 - self._last_bar_price / trade_init_price
                tradeNo = index
                # 止盈, 止损
                if tmp_ret > self.stop_win:
                    self.update_position(tradeNo, self._close_position_price, status='Off', datetime_end=self._current_bar_datetime)
                    print('Stop win at {} '.format(self._current_bar_datetime))
                elif tmp_ret < self.stop_loss:
                    self.update_position(tradeNo, self._close_position_price, status='Off', datetime_end=self._current_bar_datetime)
                    print('Stop loss at {} '.format(self._current_bar_datetime))
                else:
                    self.update_position(tradeNo, self._last_bar_price, status='On', datetime_end=None)

    def get_current_status(self):
        tradebook = self.get_position()
        if tradebook.empty:
            return True
        else:
            active_trades_num = len(tradebook.loc[tradebook.status=='On', :])
            if active_trades_num >= self.trade_unit:
                return False
            else:
                return True

    def get_position(self):
        return pd.DataFrame(self.position).T

    def get_final_position(self, end_date):
        final_position = pd.DataFrame(self.position).T
        final_position = final_position.merge(self.contract_info, left_on='contract_name', right_on='代码')

        # final_position['x_min_price'] = self.x_min_price
        final_position['real_ret'] = np.NaN
        final_position['real_init_price'] = np.NaN
        final_position['real_end_price'] = np.NaN
        final_position.loc[final_position.end_price.isnull(), 'end_price'] = self.raw_data.loc[self._current_bar_datetime, 'syl1']
        for index, row in final_position.iterrows():
            if row['position'] == 'short':
                real_init_price = row['init_price'] - self.x_min_price * row['最小变动价位']
                real_end_price = row['end_price'] + self.x_min_price * row['最小变动价位']
                real_init_amount = real_init_price * row['trade_unit'] * row['合约乘数']
                real_end_amount = real_end_price * row['trade_unit'] * row['合约乘数']
                real_init_amount_fee = real_init_amount * row['开仓手续费(交易额万分之)']/10000
                real_end_amount_fee = real_end_amount * row['平仓手续费(交易额万分之)']/10000
                real_init_num_fee = row['trade_unit'] * row['开仓手续费(元/手)']
                real_end_num_fee = row['trade_unit'] * row['平仓手续费(元/手)']
                final_position.loc[index, 'real_init_price'] = (real_init_amount - real_init_amount_fee - real_init_num_fee)/(row['trade_unit'] * row['合约乘数'])
                final_position.loc[index, 'real_end_price'] = (
                                                                           real_end_amount + real_end_amount_fee + real_end_num_fee) / \
                                                              (row['trade_unit'] * row['合约乘数'])
                final_position.loc[index, 'real_ret'] = (real_init_amount - real_end_amount - real_init_amount_fee - real_end_amount_fee - real_init_num_fee - real_end_num_fee)/real_init_amount
                final_position.loc[index, 'ret'] = 1 - final_position.loc[index, 'end_price']/final_position.loc[index, 'init_price']
            elif row['position'] == 'long':
                real_init_price = row['init_price'] + self.x_min_price * row['最小变动价位']
                real_end_price = row['end_price'] - self.x_min_price * row['最小变动价位']
                real_init_amount = real_init_price * row['trade_unit'] * row['合约乘数']
                real_end_amount = real_end_price * row['trade_unit'] * row['合约乘数']
                real_init_amount_fee = real_init_amount * row['开仓手续费(交易额万分之)']/10000
                real_end_amount_fee = real_end_amount * row['平仓手续费(交易额万分之)']/10000
                real_init_num_fee = row['trade_unit'] * row['开仓手续费(元/手)']
                real_end_num_fee = row['trade_unit'] * row['平仓手续费(元/手)']
                final_position.loc[index, 'real_init_price'] = (
                                                                    real_init_amount + real_init_amount_fee + real_init_num_fee) / \
                                                               (row['trade_unit'] * row['合约乘数'])
                final_position.loc[index, 'real_end_price'] = (
                                                                    real_end_amount - real_end_amount_fee - real_end_num_fee) / \
                                                              (row['trade_unit'] * row['合约乘数'])
                final_position.loc[index, 'real_ret'] = (real_end_amount - real_init_amount - real_init_amount_fee - real_end_amount_fee - real_init_num_fee - real_end_num_fee) / real_init_amount
                final_position.loc[index, 'ret'] = final_position.loc[index, 'end_price'] / final_position.loc[
                    index, 'init_price'] - 1
        final_position.index = final_position.datetime_start.tolist()
        end_data_datetime = datetime.datetime.strptime(end_date + ' 15:00:00', '%Y-%m-%d %H:%M:%S')
        if 'On' in final_position.status.tolist():
            final_position.loc[final_position.status=='On', 'datetime_end'] = end_data_datetime


        return final_position

    def update_position(self, count, trade_close_price, status, datetime_end):
        if self.position[count]['position'] == 'long':
            self.position[count]['ret'] = trade_close_price / self.position[count]['init_price'] - 1
        elif self.position[count]['position'] == 'short':
            self.position[count]['ret'] = 1 - trade_close_price / self.position[count]['init_price']
        self.position[count]['end_price'] = trade_close_price
        self.position[count]['status'] = status
        self.position[count]['datetime_end'] = datetime_end

    def trade_available_check(self):
        if self.new_day and self.rotate_flag:
            print('{}: time to rotate.'.format(self._current_bar_datetime))
            self.exit_long_position_rotate()
            self.exit_short_position_rotate()
            return False

        if len(self._clearing_time) > 0:
            signal = np.sum([x[0]<=self._current_bar_datetime<=x[1] for x in self._clearing_time])
            if signal > 0:
                print('{}: Enter clearing time'.format(self._current_bar_datetime))
                self.exit_short_position()
                self.exit_long_position()
                return False
            else:
                if self.trade_count_daily != self.max_trade_times:
                    return True
                else:
                    print('{}: meet trade time limit.'.format(self._current_bar_datetime))
                    return False
        else:
            if self.trade_count_daily != self.max_trade_times:
                return True
            else:
                print('{}: meet trade time limit.'.format(self._current_bar_datetime))
                return False




    def enter_long_position(self, trade_unit, position):
        # check position availability
        ckeck_signal = self.trade_available_check()
        if ckeck_signal:
            if self._current_bar_datetime>=self.next_long_datetime:
                current_pos = self.get_position()
                trade_info_dict ={
                    'contract_name': self._contract_name,
                    'trade_unit': trade_unit,
                    'init_price': self._open_position_price,
                    'end_price': None,
                    'datetime_start': self._current_bar_datetime,
                    'datetime_end': None,
                    'position': position,
                    'status': 'On',
                    'ret': 0,
                }
                if current_pos.empty:
                    # 记录交易
                    self.position[self.count] = trade_info_dict
                    self.count += 1
                    self.trade_count_daily += 1
                    print('open long position success at {}!'.format(self._current_bar_datetime))
                else:
                    # 是否有足够多仓
                    current_active_long_pos = len(current_pos.loc[(current_pos.status=='On')&(current_pos.position=='long'), :])
                    if current_active_long_pos >= self.trade_unit:
                        print('open long position fail at {} because no exceed position for long!'.format(self._current_bar_datetime))
                    elif current_active_long_pos < self.trade_unit:
                        pos_index = self.check_postion(tradebook=current_pos, position='short', current_bar_datetime=self._current_bar_datetime)
                        if pos_index:
                            self.update_position(pos_index, self._close_position_price, status='Off', datetime_end=self._current_bar_datetime)
                            self.position[self.count] = trade_info_dict
                            self.count += 1
                            self.trade_count_daily += 1
                            self.next_short_datetime = self._current_bar_datetime + datetime.timedelta(
                                seconds=self.short_freeze_period)
                            print('open long position success at {}!'.format(self._current_bar_datetime))
                        else:
                            self.position[self.count] = trade_info_dict
                            self.count += 1
                            self.trade_count_daily += 1
                            print('open long position success at {}!'.format(self._current_bar_datetime))
            else:
                print('{} long trade is frozen.'.format(self._current_bar_datetime))

    def enter_short_position(self, trade_unit, position):
        # check position availability
        ckeck_signal = self.trade_available_check()
        if ckeck_signal:
            if self._current_bar_datetime >= self.next_short_datetime:
                current_pos = self.get_position()
                trade_info_dict = {
                    'contract_name': self._contract_name,
                    'trade_unit': trade_unit,
                    'init_price': self._open_position_price,
                    'end_price': None,
                    'datetime_start': self._current_bar_datetime,
                    'datetime_end': None,
                    'position': position,
                    'status': 'On',
                    'ret': 0,
                }
                if current_pos.empty:
                    # 记录交易
                    self.position[self.count] = trade_info_dict
                    self.count += 1
                    self.trade_count_daily += 1
                    print('open short position success at {}!'.format(self._current_bar_datetime))
                else:
                    # 是否有足够多仓
                    current_active_short_pos = len(
                        current_pos.loc[(current_pos.status == 'On') & (current_pos.position == 'short'), :])
                    if current_active_short_pos >= self.trade_unit:
                        print('open short position fail at {} because no exceed position for short!'.format(
                            self._current_bar_datetime))
                    elif current_active_short_pos < self.trade_unit:
                        pos_index = self.check_postion(tradebook=current_pos, position='long',
                                                       current_bar_datetime=self._current_bar_datetime)
                        if pos_index:
                            self.update_position(pos_index, self._close_position_price, status='Off',
                                                 datetime_end=self._current_bar_datetime)
                            self.position[self.count] = trade_info_dict
                            self.count += 1
                            self.trade_count_daily += 1
                            self.next_long_datetime = self._current_bar_datetime + datetime.timedelta(
                                seconds=self.long_freeze_period)
                            print('open short position success at {}!'.format(self._current_bar_datetime))
                        else:
                            self.position[self.count] = trade_info_dict
                            self.count += 1
                            self.trade_count_daily += 1
                            print('open short position success at {}!'.format(self._current_bar_datetime))
            else:
                print('{} short trade is frozen.'.format(self._current_bar_datetime))
    def check_postion(self, tradebook, position, current_bar_datetime):
        if len(tradebook[(tradebook.position==position)&(tradebook.status=='On')]) > 0:
            if len(tradebook[(tradebook.position==position)&(tradebook.status=='On')&(tradebook.datetime_start!=current_bar_datetime)]) > 0:
                return tradebook[(tradebook.position==position)&(tradebook.status=='On')&(tradebook.datetime_start!=current_bar_datetime)].index[0]
            else:
                return False
        else:
            return False

    def exit_long_position(self,):
        current_pos = self.get_position()
        if current_pos.empty:
            # 记录交易
            pass
            print('{}: no long position to close!'.format(self._current_bar_datetime))
        else:
            pos_index = self.check_postion(tradebook=current_pos, position='long',
                                           current_bar_datetime=self._current_bar_datetime)
            if pos_index:
                self.update_position(pos_index, self._close_position_price, status='Off',
                                     datetime_end=self._current_bar_datetime)
                self.next_long_datetime = self._current_bar_datetime + datetime.timedelta(
                    seconds=self.long_freeze_period)
                print('{}: close long position success!'.format(self._current_bar_datetime))
            else:
                print('{}: no long position to close!'.format(self._current_bar_datetime))

    def exit_short_position(self,):
        current_pos = self.get_position()
        if current_pos.empty:
            # 记录交易
            pass
            print('{}: no short position to close!'.format(self._current_bar_datetime))
        else:
            pos_index = self.check_postion(tradebook=current_pos, position='short',
                                           current_bar_datetime=self._current_bar_datetime)
            if pos_index:
                self.update_position(pos_index, self._close_position_price, status='Off',
                                     datetime_end=self._current_bar_datetime)
                self.next_long_datetime = self._current_bar_datetime + datetime.timedelta(
                    seconds=self.short_freeze_period)
                print('{}: close short position success!'.format(self._current_bar_datetime))
            else:
                print('{}: no short position to close!'.format(self._current_bar_datetime))

    def exit_long_position_clearing_daily(self, price):
        current_pos = self.get_position()
        if current_pos.empty:
            # 记录交易
            pass
            print('{}: no long position to close!'.format(self._current_bar_datetime))
        else:
            pos_index = self.check_postion(tradebook=current_pos, position='long',
                                           current_bar_datetime=self._current_bar_datetime)
            if pos_index:
                self.update_position(pos_index, price, status='Off',
                                     datetime_end=self._current_bar_datetime)
                self.next_long_datetime = self._current_bar_datetime + datetime.timedelta(
                    seconds=self.long_freeze_period)
                print('{}: close long position success!'.format(self._current_bar_datetime))
            else:
                print('{}: no long position to close!'.format(self._current_bar_datetime))

    def exit_short_position_clearing_daily(self, price):
        current_pos = self.get_position()
        if current_pos.empty:
            # 记录交易
            pass
            print('{}: no short position to close!'.format(self._current_bar_datetime))
        else:
            pos_index = self.check_postion(tradebook=current_pos, position='short',
                                           current_bar_datetime=self._current_bar_datetime)
            if pos_index:
                self.update_position(pos_index, price, status='Off',
                                     datetime_end=self._current_bar_datetime)
                self.next_long_datetime = self._current_bar_datetime + datetime.timedelta(
                    seconds=self.short_freeze_period)
                print('{}: close short position success!'.format(self._current_bar_datetime))
            else:
                print('{}: no short position to close!'.format(self._current_bar_datetime))
    def exit_long_position_rotate(self,):
        current_pos = self.get_position()
        if current_pos.empty:
            # 记录交易
            pass
            print('{}: no long position to close!'.format(self._current_bar_datetime))
        else:
            pos_index = self.check_postion(tradebook=current_pos, position='long',
                                           current_bar_datetime=self._current_bar_datetime)
            if pos_index:
                close_position_price = self.raw_data_ZL_last.loc[self._current_bar_datetime, self.rotate_trade_name]
                self.update_position(pos_index, close_position_price, status='Off',
                                     datetime_end=self._current_bar_datetime)
                print('{}: close long position success!'.format(self._current_bar_datetime))
            else:
                print('{}: no long position to close!'.format(self._current_bar_datetime))

    def exit_short_position_rotate(self,):
        current_pos = self.get_position()
        if current_pos.empty:
            # 记录交易
            pass
            print('{}: no short position to close!'.format(self._current_bar_datetime))
        else:
            pos_index = self.check_postion(tradebook=current_pos, position='short',
                                           current_bar_datetime=self._current_bar_datetime)
            if pos_index:
                close_position_price = self.raw_data_ZL_last.loc[self._current_bar_datetime, self.rotate_trade_name]
                self.update_position(pos_index, close_position_price, status='Off',
                                     datetime_end=self._current_bar_datetime)
                print('{}: close short position success!'.format(self._current_bar_datetime))
            else:
                print('{}: no short position to close!'.format(self._current_bar_datetime))
                

